#include "NetworkManager.h"
#include "Game.h"
#include "Size.h"
#include "Level2.h"
#include "GLUtils.h"
#include "AudioManager.h"
#include "Logger.h"
#include "Exception.h"

using namespace game_engine;
using namespace game_engine::open_gl;
using namespace game_engine::utils;

bool Game::instanceFlag = false;
Game* Game::instance = NULL;

const string CLIENT_TOKEN = "client";
const string SERVER_TOKEN = "server";

Scene* Game::currentScene = NULL;
vector<game_engine::Scene*> Game::scenes;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

Game::~Game(void)
{
    instanceFlag = false;
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

Game* Game::getInstance(void)
{
    if(!instanceFlag)
    {
        instance = new Game();
        instanceFlag = true;
        return instance;
    }
    else
    {
        return instance;
    }
}

void Game::init(INT32 argc, char** argv) const throw(...)
{
    // Verify number of arguments
    if (argc != 2)
    {
        string err("incorrect number of arguments!!\n");
        err.append("\tDemo2 <client|server>");
        throw RuntimeException(err);
    }
    string role(argv[1]);
    BOOL isServer = false;
    Logger::INFO(GAME_LOG, string ("role : ") + role);
    if (role == SERVER_TOKEN)
    {
        isServer = true;
    }
    else if (role != CLIENT_TOKEN)
    {
        string err("unknown role specified");
        throw RuntimeException(err);
    }

    // Initialize the graphic framework
    GLUtils::mainInit(argc, argv, "DEMO2 - " + role, Game::render,
        Game::readKey, Game::readSpecialKey, Game::readKeyUp, 
        Game::readSpecialKeyUp, Game::readMouse, Game::loop);
    
    // Initialize the audio system
    AudioManager *audioManager = AudioManager::getInstance();

    // Initialize the network system
    NetworkManager *networkManager = NetworkManager::getInstance();
    networkManager->initialize(isServer);

    // Create an object from class Level2
    Level2 *level2 = new Level2(isServer);
    level2->setResolution(Size(512, 640));
    level2->SetNetworkIDManager(&(networkManager->getNetworkIDManager()));
    level2->SetNetworkID(LEVEL_ID);

    scenes.push_back(level2);
    currentScene = level2;
    currentScene->initialize();
}

void Game::run(void) const
{
	// Application loop
	GLUtils::startEventLoop();
}

void Game::loop(void)
{
    BOOL res = false;
    try
    {
        res = currentScene->loop();
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::loop : ") + e.what());
    }

    if (!res)
    {
        finalize();
    }
}

void Game::finalize(void)
{
    try
    {
        currentScene->finalize();

        // Shutdown the audio system
        AudioManager *audioManager = AudioManager::getInstance();
        delete audioManager;

        // Shutdown the network system
        NetworkManager *networkManager = NetworkManager::getInstance();
        delete networkManager;

	    cout << "Press ENTER to close the game ...";
	    getline(cin, string());
        exit(0);
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::finalize : ") + e.what());
    }
}

void Game::readKey(const UCHAR key, const INT32 x, const INT32 y)
{
    try
    {
        currentScene->readKeyboard(key, x, y, true);
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::readKeyboard : ") + e.what());
    }
}

void Game::readSpecialKey(const INT32 key, const INT32 x, const INT32 y)
{
    try
    {
        currentScene->readKeyboard(static_cast<UCHAR>(key), x, y, true);
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::readSpecialKey : ") + e.what());
    }
}

void Game::readKeyUp(const UCHAR key, const INT32 x, const INT32 y)
{
    try
    {
        currentScene->readKeyboard(key, x, y, false);
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::readKeyUp : ") + e.what());
    }
}

void Game::readSpecialKeyUp(const INT32 key, const INT32 x, const INT32 y)
{
    try
    {
        currentScene->readKeyboard(static_cast<UCHAR>(key), x, y, false);
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::readSpecialKeyUp : ") + e.what());
    }
}

void Game::readMouse(const INT32 button, const INT32 state, 
                      const INT32 x, const INT32 y)
{
    try
    {
        currentScene->readMouse(button, state, x, y);
    } catch (exception &e)
    {
        cout << "exception in Game::readMouse : " << e.what();
    }
}

void Game::render(void)
{
    try
    {
        currentScene->render();
    } catch (exception &e)
    {
        Logger::FATAL(GAME_LOG, string("exception in Game::render : ") + e.what());
    }
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

Game::Game(void)
{
}

